package cn.edu.ntu.websocket;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;

import cn.edu.ntu.constant.TypeEnum;
import cn.edu.ntu.entity.Content;
import cn.edu.ntu.entity.WSMessage;
import cn.edu.ntu.entity.WSResult;
import cn.edu.ntu.entity.WSUser;
import cn.edu.ntu.utils.Constant;
import cn.edu.ntu.utils.Constant.MESSAGE_TYPE;
import cn.edu.ntu.utils.JsonUtils;

@ServerEndpoint("/chat")
public class WSChatServer {

	private static final Logger logger = LoggerFactory.getLogger(WSChatServer.class);
	
	private static Map<String, List<Content>> contentListMap = new ConcurrentHashMap<>();
	private static final String DEFAULT_ROOM = "DEFAULT_ROOM";
	
	private static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
	
	private static Map<String, String> sessionIdAndUserMap = new ConcurrentHashMap<>();
	
	private static List<WSUser> userList = new CopyOnWriteArrayList<>();
	
	@OnOpen
	public void onOpen(Session session) throws IOException {
		logger.info("session id : " + session.getId() + " 已经上线！");
	}
	 
	@OnMessage
	public void onMessage(String message, Session session) throws IOException {
		WSMessage wsMessage = JsonUtils.toObject(message, WSMessage.class);
		if(TypeEnum.HEART.getName().equals(wsMessage.getType())){
			return;
		}
		if(TypeEnum.CONNECT.getName().equals(wsMessage.getType())){
			//判断是否重复登录
			Session preSession = sessionMap.get(wsMessage.getId());
			if(preSession != null){
				if(preSession.isOpen()){
					WSResult result = new WSResult();
					result.setType(MESSAGE_TYPE.REPEAT_LOGIN);
					result.setContent("请不要在同个浏览器下重复登录！");
					session.getBasicRemote().sendText(JsonUtils.toJson(result));
					return;
				}
			}
			WSUser user = new WSUser();
			user.setId(wsMessage.getId());
			user.setName(wsMessage.getName());
			userList.add(user);
			sessionMap.put(wsMessage.getId(), session);
			sessionIdAndUserMap.put(session.getId(), wsMessage.getId());
			
			//登录返回
			loginReturnInfo(session, wsMessage);
			//登录通知
			remindLogin(user);
			
		}else if(TypeEnum.ALL.getName().equals(wsMessage.getType())){
			WSResult result = new WSResult();
			result.setType(Constant.MESSAGE_TYPE.NORMAL);
			result.setFrom(wsMessage.getName());
			result.setContent(wsMessage.getContent());
			result.setSendTime(new Date());
			broadcast(sessionMap, wsMessage.getId(), result);
			
			//storeMessageIntoContentListMap(wsMessage);
		}else if(TypeEnum.ONE_RECORDS.getName().equals(wsMessage.getType())){
			WSResult result = new WSResult();
			result.setType(Constant.MESSAGE_TYPE.ONE_RECORDS);
			result.setContentList(null);
			sendMessgae(session, result);
		}else if(TypeEnum.ONE.getName().equals(wsMessage.getType())){
			WSResult result = new WSResult();
			result.setType(Constant.MESSAGE_TYPE.ONE);
			result.setId(wsMessage.getId());
			result.setName(wsMessage.getName());
			result.setFrom(wsMessage.getName());
			result.setTo(wsMessage.getToUserId());
			result.setContent(wsMessage.getContent());
			result.setSendTime(new Date());
			
			Session toSession = sessionMap.get(wsMessage.getToUserId());
			if(toSession.isOpen()){
				sendMessgae(toSession, result);
			}else{
				// TODO  用户不在线不发送数据保存在消息中间件，等用户下次登录可以进行数据推送
			}
		}
		
	}
	
	/**
	 *    这里要说一下上面的onClose方法，这个方法里面：
	 *	        第一不要使用session发送消息给用户
	 *	        第二也不要手动调用close方法
		        第三这里面不能有任何异常抛出
		    以上三种情况都会引起Message will not be sent because the WebSocket session has been closed
	 * @param session
	 * @param reason
	 */
	@OnClose
	public void onClose(Session session, CloseReason reason) {
		String id = sessionIdAndUserMap.get(session.getId());
		if(StringUtils.isNotBlank(id)){
			removeSomeoneFromUserList(id);
		}
	} 
	
	/**
	 * 广播消息
	 * @param sessionMap   session对象
	 */
	private void broadcast(Map<String, Session> sessionMap, String id, WSResult message) {
		if(StringUtils.isEmpty(id)){
			return;
		}
		for(Map.Entry<String, Session> entry : sessionMap.entrySet()){
			if(!id.equals(entry.getKey())){
				sendMessgae(entry.getValue(), message);
			}
		}
	}
	
	/**
	 * 发送消息
	 * @param session
	 * @param message
	 */
	private void sendMessgae(Session session, WSResult message) {
		try {
			if(session.isOpen()){
				session.getBasicRemote().sendText(JsonUtils.toJson(message));
			}
			//如果session处于closed状态，可以加入activemq中，回头对方登录之后再推送
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 用户登录成功返回对应的数据
	 * @param session
	 */
	private void loginReturnInfo(Session session, WSMessage message) {
		WSResult result = new WSResult();
		result.setType(MESSAGE_TYPE.LOGIN_RETURN);
		result.setId(message.getId());
		result.setName(message.getName());
		result.setSendTime(new Date());
		result.setUserList(userList);
		result.setContentList(contentListMap.get(DEFAULT_ROOM));
		try {
			session.getBasicRemote().sendText(JsonUtils.toJson(result));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	
	/**
	 * 登录提醒
	 * @param number  登录用户
	 */
	private void remindLogin(WSUser user){
		WSResult message = new WSResult();
		message.setType(Constant.MESSAGE_TYPE.LOGIN);
		message.setContent(user.getName() + "  上线啦");
		message.setSendTime(new Date());
		message.setId(user.getId());
		message.setName(user.getName());
		broadcast(sessionMap, user.getId(), message);
	}
	
	/**
	 * 登录提醒提醒
	 * @param number  登录用户
	 */
	private void remindLoginOut(WSUser user){
		WSResult message = new WSResult();
		message.setType(Constant.MESSAGE_TYPE.LOGIN_OUT);
		message.setSendTime(new Date());
		message.setId(user.getId());
		message.setName(user.getName());
		broadcast(sessionMap, user.getId(), message);
	}
	
	/**
	 * 重用户列表中移除用户
	 * @param id
	 * @return 
	 */
	private void removeSomeoneFromUserList(String id){
		Iterator<WSUser> userIterator = userList.iterator();
		while(userIterator.hasNext()){
			WSUser tempUser = userIterator.next();
			if(id.equals(tempUser.getId())){
				logger.info("用户： " + tempUser.getName() + " 下线了！");
				remindLoginOut(tempUser);
				userList.remove(tempUser);
			}
		}
	}
	
	/**
	 * 保存消息
	 * @param wsMessage
	 */
	private void storeMessageIntoContentListMap(WSMessage wsMessage) {
		Content content = new Content();
		content.setType("1");
		content.setId(wsMessage.getId());
		content.setName(wsMessage.getName());
		content.setContent(wsMessage.getContent());
		content.setCreateTime(new Date());
		
		List<Content> contentList = contentListMap.get(DEFAULT_ROOM);
		if(CollectionUtils.isEmpty(contentList)){
			contentList = new ArrayList<Content>();
		}
		contentList.add(content);
		contentListMap.put(DEFAULT_ROOM, contentList);
	}

}
